package cn.gs.global.server.account;

// @author gs
import cn.gs.algorithm.NumberSplitUtil;
import cn.gs.db.HibernateUtil;
import cn.gs.db.BoolTransaction;
import cn.gs.db.ObjectTransaction;
import cn.gs.global.common.CntPriceInfo;
import cn.gs.global.common.account.IAccountServer;
import cn.gs.global.table.account.SimKeep;
import cn.gs.global.table.account.SimOgn;
import cn.gs.global.table.account.StockKeep;
import cn.gs.global.table.exchg.Dividend;
import cn.gs.global.table.exchg.Exchange;
import cn.gs.global.table.exchg.ObjExchg;
import cn.gs.global.table.exchg.StockExchg;
import java.text.MessageFormat;
import org.hibernate.Query;

public class AccountServer implements IAccountServer {

    final HibernateUtil util;
    final AccountCore core;

    public AccountServer(AccountCore core) {
        this.core = core;
        this.util = core.getUtil();
    }

    @Override
    public boolean dividend(final int ognid, final long money) {
        return util.transaction(new BoolTransaction() {

            @Override
            public boolean work() {
                SimOgn ogn = getOgn(ognid);
                if (ogn.getCash() >= money) {
                    CntPriceInfo info = stockOffered(ognid);
                    long[] ass = NumberSplitUtil.split(money, info.getCnt());
                    for (int i = 0, len = info.getId().length; i < len; ++i) {
                        Dividend d = new Dividend();
                        fillExchange(d, ognid, info.getId()[i], ass[i]);
                        util.saveOrUpdate(d);
                    }
                    return true;
                } else {
                    return false;
                }
            }
        });
    }

    @Override
    public CntPriceInfo stockOffered(final int ognid) {
        return (CntPriceInfo) util.transaction(new ObjectTransaction() {

            @Override
            public Object work() {
                SimOgn ogn = getOgn(ognid);
                Query q = util.query("select ogn, cnt, cost from StockKeep where keeped=:ogn");
                q.setParameter("ogn", ogn);
                return new CntPriceInfo(q);
            }
        });
    }

    @Override
    public CntPriceInfo boughtStockOf(int ognid, int begin, int end, Object... ids) {
        return exchgeOf("select keeped, sum(cnt), sum(money) from StockExchg where frm=:ogn and keeped in :ids and extime between :begin and :end group by keep", ognid, begin, end, ids);
    }

    @Override
    public CntPriceInfo saledStockOf(int ognid, int begin, int end, Object... ids) {
        return exchgeOf("select keeped, sum(cnt), sum(money) from StockExchg where too=:ogn and keeped in :ids and extime between :begin and :end group by keep", ognid, begin, end, ids);
    }

    @Override
    public CntPriceInfo boughtObjOf(int ognid, int begin, int end, Object... objs) {
        return exchgeOf("select obj, sum(cnt), sum(money) from ObjExchg where frm=:ogn and obj in :ids and extime between :begin and :end group by obj", ognid, begin, end, objs);
    }

    @Override
    public CntPriceInfo saledObjOf(int ognid, int begin, int end, Object... objs) {
        return exchgeOf("select obj, sum(cnt), sum(money) from ObjExchg where too=:ogn and obj in :ids and extime between :begin and :end group by obj", ognid, begin, end, objs);
    }

    @Override
    public CntPriceInfo costStockOf(final int ognid, final Object... ogns) {
        return (CntPriceInfo) util.transaction(new ObjectTransaction() {

            @Override
            public Object work() {
                SimOgn ogn = getOgn(ognid);
                Query q = util.query("from StockKeep where ogn=:ogn and keeped in :ogns");
                q.setParameter("ogn", ogn);
                q.setParameterList("ogns", ogns);
                return new CntPriceInfo(q);
            }
        });
    }

    @Override
    public CntPriceInfo costObjOf(final int ognid, final Object... objs) {
        return (CntPriceInfo) util.transaction(new ObjectTransaction() {

            @Override
            public Object work() {
                SimOgn ogn = getOgn(ognid);
                Query q = util.query("select obj, cnt, buy+produce from SimKeep where ogn=:ogn and obj in :objs");
                q.setParameter("ogn", ogn);
                q.setParameterList("objs", objs);
                return new CntPriceInfo(q);
            }
        });
    }

    @Override
    public boolean exchange(final int frm, final int too, final long money) {
        return util.transaction(new BoolTransaction() {

            @Override
            public boolean work() {
                Exchange ex = new Exchange();
                boolean b = fillExchange(ex, frm, too, money);
                util.saveOrUpdate(ex);
                return b;
            }
        });
    }

    @Override
    public boolean buyStock(final int frm, final int too, final long money, final int ognid, final int cnt) {
        if (cnt > 0) {
            return util.transaction(new BoolTransaction() {

                @Override
                public boolean work() {
                    StockExchg ex = new StockExchg();
                    boolean b = fillExchange(ex, frm, too, money);
                    ex.setCnt(cnt);
                    SimOgn ogn = getOgn(ognid);
                    ex.setOgn(ogn);
                    util.saveOrUpdate(ex);
                    StockKeep keep = getStockKeep(frm, ognid);
                    keep.setCnt(keep.getCnt() + cnt);
                    keep.setCost(keep.getCost() + money);
                    util.saveOrUpdate(keep);
                    return b;
                }
            });
        } else {
            return exchange(frm, too, money);
        }
    }

    @Override
    public boolean buyObj(final int frm, final int too, final long money, final int obj, final int cnt) {
        if (cnt > 0) {

            return util.transaction(new BoolTransaction() {

                @Override
                public boolean work() {
                    ObjExchg ex = new ObjExchg();
                    boolean b = fillExchange(ex, frm, too, money);
                    ex.setCnt(cnt);
                    ex.setObj(obj);
                    try {
                        util.saveOrUpdate(ex); // org.hibernate.exception.ConstraintViolationException: Duplicate entry '3' for key 'PRIMARY'
                    } catch (Throwable eu) {
                        System.out.println(ex.toString());
                        eu.printStackTrace();
                        System.exit(1);
                    }
                    SimKeep keep = getKeep(frm, obj);
                    keep.setBuy(keep.getBuy() + money);
                    keep.setPerPrice(money / (double) cnt);
                    util.saveOrUpdate(keep);
                    return b;
                }
            });
        } else {
            return exchange(frm, too, money);
        }
    }

    CntPriceInfo exchgeOf(final String hql, final int ognid, final int begin, final int end, final Object... ids) {
        return (CntPriceInfo) util.transaction(new ObjectTransaction() {

            @Override
            public Object work() {
                SimOgn ogn = getOgn(ognid);
                Query q = util.query(hql);
                q.setParameter("ogn", ogn);
                q.setParameterList("ids", ids);
                q.setParameter("begin", begin);
                q.setParameter("end", end);
                return new CntPriceInfo(q);
            }
        });
    }

    StockKeep getStockKeep(int ogn, int ko) {
        Query q = util.query("from StockKeep where ogn=:ogn and keep:=keep");
        q.setParameter("ogn", ogn);
        q.setParameter("keep", ko);
        StockKeep k = (StockKeep) q.uniqueResult();
        if (null == k) {
            k = new StockKeep();
            k.setOgn(ogn);
            k.setKeeped(ko);
            util.saveOrUpdate(k);
        }
        return k;
    }

    SimKeep getKeep(int ogn, int obj) {
        Query q = util.query("from SimKeep where ogn=:ogn and obj=:obj");
        q.setParameter("ogn", ogn);
        q.setParameter("obj", obj);
        SimKeep keep = (SimKeep) q.uniqueResult();
        if (null == keep) {
            keep = new SimKeep();
            keep.setOgn(ogn);
            keep.setObj(obj);
            util.saveOrUpdate(keep);
        }
        return keep;
    }

    SimOgn getOgn(int id) {
//        if (id <= 0) {
//            return null;
//        }
        SimOgn ogn = util.get(SimOgn.class, id);
        if (null == ogn) {
            ogn = new SimOgn();
            ogn.setId(id);
            util.saveOrUpdate(ogn);
        }
        return ogn;
    }

    boolean cashFlow(int frm, int too, long money) {
        SimOgn ofrm;
        SimOgn otoo;
        if (frm < too) {
            ofrm = getOgn(frm);
            otoo = getOgn(too);
        } else {
            otoo = getOgn(too);
            ofrm = getOgn(frm);
        }
        if (ofrm != null) {
            ofrm.setCash(ofrm.getCash() - money);
            util.saveOrUpdate(ofrm);
        }
        if (otoo != null) {
            otoo.setCash(otoo.getCash() + money);
            util.saveOrUpdate(otoo);
        }
        return null == ofrm ? true : ofrm.getCash() >= 0;
    }

    boolean fillExchange(Exchange ex, int frm, int too, long money) {
        ex.setMoney(money);
        ex.setFrm(frm);
        ex.setToo(too);
        ex.setExtime(core.date());
        return cashFlow(frm, too, money);
    }
}
